﻿using System;
using System.Security.Cryptography;
using System.IO;

using Fuse8.DomainFramework.Common;

namespace Fuse8.DomainFramework.Common.Utilities
{
    /// <summary>
    /// 
    /// </summary>
    public static class CryptoUtility
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="includePrivateParameters"></param>
        /// <returns></returns>
        public static string GenerateRsaKey(bool includePrivateParameters)
        {
            string result = string.Empty;

            try
            {
                using (RSACryptoServiceProvider rsaCryptoServiceProvider = new RSACryptoServiceProvider())
                {
                    result = rsaCryptoServiceProvider.ToXmlString(includePrivateParameters);
                }
            }
            catch (CryptographicException)
            { }

            return result;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="rsaKeyInfo"></param>
        /// <returns></returns>
        public static string ExportPublicKey(string rsaKeyInfo)
        {
            Guard.ArgumentNotEmpty(rsaKeyInfo, "rsaKeyInfo");

            string result = string.Empty;

            try
            {
                using (RSACryptoServiceProvider rsaCryptoServiceProvider = new RSACryptoServiceProvider())
                {
                    rsaCryptoServiceProvider.FromXmlString(rsaKeyInfo);

                    if (!rsaCryptoServiceProvider.PublicOnly)
                    {
                        result = rsaCryptoServiceProvider.ToXmlString(false);
                    }
                }
            }
            catch (CryptographicException)
            { }

            return result;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public static DesCryptoContainer CreateTripleDesKey()
        {
            DesCryptoContainer result = null;

            try
            {
                using (TripleDESCryptoServiceProvider tripleDESCryptoServiceProvider = new TripleDESCryptoServiceProvider())
                {
                    result = new DesCryptoContainer
                            {
                                Key = tripleDESCryptoServiceProvider.Key,
                                IV = tripleDESCryptoServiceProvider.IV
                            };
                }
            }
            catch (CryptographicException)
            { }

            return result;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="dataToEncrypt"></param>
        /// <param name="rsaKeyInfo"></param>
        /// <returns></returns>
        public static byte[] RsaEncrypt(byte[] dataToEncrypt, string rsaKeyInfo)
        {
            Guard.ArgumentNotNull(dataToEncrypt, "dataToEncrypt");
            Guard.ArgumentNotEmpty(rsaKeyInfo, "rsaKeyInfo");
            
            byte[] encrypt = null;

            try
            {
                using (RSACryptoServiceProvider rsaCryptoServiceProvider = new RSACryptoServiceProvider())
                {
                    rsaCryptoServiceProvider.FromXmlString(rsaKeyInfo);
                    encrypt = rsaCryptoServiceProvider.Encrypt(dataToEncrypt, true);
                }
            }
            catch (CryptographicException)
            { }

            return encrypt;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="dataToDecrypt"></param>
        /// <param name="rsaKeyInfo"></param>
        /// <returns></returns>
        public static byte[] RsaDecrypt(byte[] dataToDecrypt, string rsaKeyInfo)
        {
            Guard.ArgumentNotNull(dataToDecrypt, "dataToDecrypt");
            Guard.ArgumentNotEmpty(rsaKeyInfo, "rsaKeyInfo");
            
            byte[] decrypt = null;

            try
            {
                using (RSACryptoServiceProvider rsaCryptoServiceProvider = new RSACryptoServiceProvider())
                {
                    rsaCryptoServiceProvider.FromXmlString(rsaKeyInfo);
                    decrypt = rsaCryptoServiceProvider.Decrypt(dataToDecrypt, true);
                }
            }
            catch (CryptographicException)
            { }

            return decrypt;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="buffer"></param>
        /// <param name="tdesKey"></param>
        /// <param name="tdesIV"></param>
        /// <returns></returns>
        public static byte[] DesEncrypt(byte[] buffer, byte[] tdesKey, byte[] tdesIV)
        {
            Guard.ArgumentNotNull(buffer, "buffer");
            Guard.ArgumentNotNull(tdesKey, "tdesKey");
            Guard.ArgumentNotNull(tdesIV, "tdesIV");

            byte[] result = null;

            try
            {
                using (TripleDESCryptoServiceProvider tripleDESCryptoServiceProvider = new TripleDESCryptoServiceProvider())
                {
                    using (MemoryStream stream = new MemoryStream())
                    {
                        using (CryptoStream cryptoStream = new CryptoStream(stream,
                            tripleDESCryptoServiceProvider.CreateEncryptor(tdesKey, tdesIV), CryptoStreamMode.Write))
                        {
                            cryptoStream.Write(buffer, 0, buffer.Length);
                            cryptoStream.FlushFinalBlock();

                            result = stream.ToArray();
                        }
                    }
                }
            }
            catch (CryptographicException)
            { }

            return result;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="buffer"></param>
        /// <param name="tdesKey"></param>
        /// <param name="tdesIV"></param>
        /// <returns></returns>
        public static byte[] DesDecrypt(byte[] buffer, byte[] tdesKey, byte[] tdesIV)
        {
            Guard.ArgumentNotNull(buffer, "buffer");
            Guard.ArgumentNotNull(tdesKey, "tdesKey");
            Guard.ArgumentNotNull(tdesIV, "tdesIV");

            byte[] result = new byte[buffer.Length];

            try
            {
                using (TripleDESCryptoServiceProvider tripleDESCryptoServiceProvider = new TripleDESCryptoServiceProvider())
                {
                    using (MemoryStream stream = new MemoryStream(buffer))
                    {
                        using (CryptoStream cryptoStream = new CryptoStream(stream,
                            tripleDESCryptoServiceProvider.CreateDecryptor(tdesKey, tdesIV), CryptoStreamMode.Read))
                        {
                            cryptoStream.Read(result, 0, result.Length);
                        }
                    }
                }
            }
            catch (CryptographicException)
            { }

            return result;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="valueToHash"></param>
        /// <returns></returns>
        public static byte[] CreateHash(byte[] valueToHash)
        {
            Guard.ArgumentNotNull(valueToHash, "valueToHash");

            byte[] hashValue = null;

            using (SHA1Managed sha1Managed = new SHA1Managed())
            {
                hashValue = sha1Managed.ComputeHash(valueToHash);
            }

            return hashValue;
        }
        
        /// <summary>
        /// 
        /// </summary>
        /// <param name="hash"></param>
        /// <param name="valueToVerify"></param>
        /// <returns></returns>
        public static bool VerifyHash(byte[] hash, byte[] valueToVerify)
        {
            Guard.ArgumentNotNull(hash, "hash");
            Guard.ArgumentNotNull(valueToVerify, "valueToVerify");

            bool result = false;
            
            byte[] hashValue = null;

            using (SHA1Managed sha1Managed = new SHA1Managed())
            {
                hashValue = sha1Managed.ComputeHash(valueToVerify);
            }

            result = (hashValue != null) && (hashValue.Length == hash.Length);

            if (result)
            {
                for (int i = 0; i < hash.Length; i++)
                {
                    if (hash[i] != hashValue[i])
                    {
                        result = false;
                        break;
                    }
                }
            }

            return result;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="hash"></param>
        /// <param name="rsaKeyInfo"></param>
        /// <returns></returns>
        public static byte[] CreateSign(byte[] hash, string rsaKeyInfo)
        {
            Guard.ArgumentNotNull(hash, "hash");
            Guard.ArgumentNotEmpty(rsaKeyInfo, "rsaKeyInfo");
            
            byte[] signedHashValue = null;

            try
            {
                using (RSACryptoServiceProvider rsaCryptoServiceProvider = new RSACryptoServiceProvider())
                {
                    rsaCryptoServiceProvider.FromXmlString(rsaKeyInfo);

                    var rsaFormatter = new RSAPKCS1SignatureFormatter(rsaCryptoServiceProvider);
                    rsaFormatter.SetHashAlgorithm("SHA1");

                    signedHashValue = rsaFormatter.CreateSignature(hash);
                }
            }
            catch (CryptographicException)
            { }            

            return signedHashValue;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="signedHash"></param>
        /// <param name="valueToVerify"></param>
        /// <param name="rsaKeyInfo"></param>
        /// <returns></returns>
        public static bool VerifySign(byte[] signedHash, byte[] valueToVerify, string rsaKeyInfo)
        {
            Guard.ArgumentNotNull(signedHash, "signedHash");
            Guard.ArgumentNotNull(valueToVerify, "valueToVerify");
            Guard.ArgumentNotEmpty(rsaKeyInfo, "rsaKeyInfo");
            
            bool result = false;

            try
            {
                using (RSACryptoServiceProvider rsaCryptoServiceProvider = new RSACryptoServiceProvider())
                {
                    rsaCryptoServiceProvider.FromXmlString(rsaKeyInfo);

                    var rsaDeformatter = new RSAPKCS1SignatureDeformatter(rsaCryptoServiceProvider);
                    rsaDeformatter.SetHashAlgorithm("SHA1");

                    result = rsaDeformatter.VerifySignature(valueToVerify, signedHash);
                }
            }
            catch (CryptographicException)
            { }

            return result;
        }
    }

    /// <summary>
    /// 
    /// </summary>
    public class DesCryptoContainer
    {
        /// <summary>
        /// 
        /// </summary>
        public byte[] Key { get; set; }

        /// <summary>
        /// 
        /// </summary>
        public byte[] IV { get; set; }

        /// <summary>
        /// 
        /// </summary>
        public DesCryptoContainer()
        { }
    }
}
